import EventEmitter from 'events';
import {Events} from 'core';

class Scheduler extends EventEmitter {
    constructor(engine, config) {
        super();

        this.engine = engine;
        this.config = config;
        this.bufMgr = null;
        this.peerMap = new Map();                 // remotePeerId -> dc
        this.bitset = new Set();                  // 本节点的bitfield
        this.bitCounts = new Map();               // 记录peers的每个buffer的总和，用于BT的rearest first策略  index -> count

        this.targetPeer = null;               // 当前的目标peer

        // 传输控制(单位s)
        this.lastFragDuration = 3;            // 默认3秒

        this._setupEngine();
    }

    updateLoadedSN(sn) {
        this.bitset.add(sn);                      //在bitset中记录
        if (this.bitCounts.has(sn)) {
            this.bitCounts.delete(sn)             //在bitCounts清除，防止重复下载
        }
        const msg = {
            event: Events.DC_HAVE,
            sn: sn
        };
        this._broadcastToPeers(msg);
    }

    updatePlaySN(sn) {
        const { logger } = this.engine;
        if (this.config.live) return;                                                 //rearest first只用于vod
        if (!this.hasPeers) return;
        // let requested = [];
        const requestedPeers = [];
        const { prefetchOffset, prefetchNum } = this.config;
        for (let idx=sn+prefetchOffset;idx<sn+prefetchOffset+prefetchNum;idx++) {
            if (!this.bitset.has(idx) && idx !== this.loadingSN && this.bitCounts.has(idx)) {                  //如果这个块没有缓存并且peers有
                for (let peer of this.peerMap.values()) {                           //找到拥有这个块并且空闲的peer
                    if (!requestedPeers.includes(peer) && peer.isAvailable && peer.bitset.has(idx)) {
                        peer.requestDataBySN(idx, false);
                        logger.debug(`request prefetch ${idx} from peer ${peer.remotePeerId}`);
                        requestedPeers.push(peer);
                        // requested.push(idx);
                        break;
                    }
                }
            }
        }

        //检查是否有空闲的节点，有的话采用rearest first策略下载(由于上行带宽有限，暂停这个策略)
        // let idlePeers = this._getIdlePeer();
        // if (idlePeers.length === 0 || this.bitCounts.size === 0 || this.bufMgr.overflowed) return;        //缓存溢出则停止rearest first
        // let sortedArr = [...this.bitCounts.entries()].sort((item1, item2) => {
        //     return item1[1] < item2[1];
        // });
        // if (sortedArr.length === 0) return;
        // //每次只下载一个rearest块
        // let rearest = sortedArr.pop()[0];
        // while (rearest === this.loadingSN || requested.includes(rearest)) {         //排除掉loading的和requested的
        //     if (sortedArr.length === 0) return;
        //     rearest = sortedArr.pop()[0];
        // }
        // for (let peer of idlePeers) {
        //     if (peer.bitset.has(rearest)) {
        //         peer.requestDataBySN(rearest, false);
        //         logger.debug(`request rearest ${rearest} from peer ${peer.remotePeerId}`);
        //         break;
        //     }
        // }

    }

    // 阻止其它peer的请求
    chokePeerRequest(dc) {
        const msg = {
            event: Events.DC_CHOKE
        };
        if (dc) {
            dc.sendJson(msg)
        } else {
            this._broadcastToPeers(msg);
        }
    }

    // 允许其它peer的请求
    unchokePeerRequest(dc) {
        const msg = {
            event: Events.DC_UNCHOKE
        };
        if (dc) {
            dc.sendJson(msg)
        } else {
            this._broadcastToPeers(msg);
        }
    }

    // 暂停从其它peer请求数据
    stopRequestFromPeers() {
        for (let peer of this.peerMap.values()) {
            peer.choked = true;
        }
    }

    // 恢复从其它peer请求数据
    resumeRequestFromPeers() {
        for (let peer of this.peerMap.values()) {
            peer.choked = false;
        }
    }

    deletePeer(dc) {
        if (this.peerMap.has(dc.remotePeerId)) {
            dc.bitset.forEach( value => {
                this._decreBitCounts(value);
            });
            this.peerMap.delete(dc.remotePeerId);
        }
        this.engine.emit('peers', [...this.peerMap.keys()]);
    }

    handshakePeer(dc) {
        this._setupDC(dc);
        dc.sendBitField(Array.from(this.bitset))            //向peer发送bitfield
    }

    addPeer(peer) {
        const { logger } = this.engine;
        logger.info(`add peer ${peer.remotePeerId}`);
        this.peerMap.set(peer.remotePeerId, peer);

        this.engine.emit('peers', [...this.peerMap.keys()]);
    }

    peersHasSN(sn) {
        return this.bitCounts.has(sn);
    }

    hasAndSetTargetPeer(sn) {
        const { logger } = this.engine;
        if (!(this.hasIdlePeers && this.peersHasSN(sn))) return false;
        for (let peer of this.peerMap.values()) {
            if (peer.isAvailable && peer.bitset.has(sn)) {
                logger.info(`found sn ${sn} from peer ${peer.remotePeerId}`);
                this.targetPeer = peer;
                return true;
            }
        }
        logger.warn(`idle peers hasn't sn ${sn}`);
        return false;
    }

    load(context, config, callbacks) {
        const { logger } = this.engine;
        this.context = context;
        const frag = context.frag;
        // console.warn(`loadTimeout ${this.config.loadTimeoutRate * frag.duration * 1000}`);
        const segId = this.config.segmentId(frag.level, frag.sn, frag.url);
        this.callbacks = callbacks;
        this.stats = {trequest: performance.now(), retry: 0, tfirst: 0, tload: 0, loaded: 0};
        this.criticalSeg = {sn: frag.sn, segId};
        this.targetPeer.requestDataById(segId, true);
        logger.info(`request criticalSeg segId ${segId} at ${frag.sn}`);
        const loadTimeout = this.config.loadTimeoutRate * this.lastFragDuration;
        // console.warn(`lastFragDuration ${this.lastFragDuration} loadTimeout ${loadTimeout} current frag duration ${frag.duration}`);
        this.criticaltimeouter = window.setTimeout(this._criticaltimeout.bind(this), loadTimeout * 1000);
    }

    get hasPeers() {
        return this.peersNum > 0;
    }

    get peersNum() {
        return this.peerMap.size;
    }

    get hasIdlePeers() {
        const { logger } = this.engine;
        const idles = this._getIdlePeer().length;
        logger.info(`peers: ${this.peersNum} idle peers: ${idles}`);
        return idles > 0;
    }

    set bufferManager(bm) {
        this.bufMgr = bm;

        bm.on(Events.BM_LOST, sn => {
            this._broadcastToPeers({                                //向peers广播已经不缓存的sn
                event: Events.DC_LOST,
                sn: sn
            });
            this.bitset.delete(sn);
        })
    }

    set nextFragLoadTime(val) {
        this.lastFragDuration = val;
        // console.warn(`lastFragDuration ${this.lastFragDuration}`);
    }

    destroy() {
        const { logger } = this.engine;
        if (this.peersNum > 0) {
            // for (let peer of this.peerMap.values()) {
            //     peer.destroy();
            //     peer = null;
            // }
            this.peerMap.clear();
        }
        this.removeAllListeners();
        logger.warn(`destroy scheduler`);
    }

    _setupDC(dc) {
        const { logger } = this.engine;
        dc.on(Events.DC_BITFIELD, msg => {
            if (!msg.field) return;
            let bitset = new Set(msg.field);
            dc.bitset = bitset;
            msg.field.forEach( value => {
                if (!this.bitset.has(value)) {              //防止重复下载
                    this._increBitCounts(value);
                }
            });
            this.addPeer(dc);                               //只有获取bitfield之后才加入peerMap
        })
            .on(Events.DC_HAVE, msg => {
                if (!msg.sn || !dc.bitset) return;
                const sn = msg.sn;
                dc.bitset.add(sn);
                if (!this.bitset.has(sn)) {              //防止重复下载
                    this._increBitCounts(sn);
                }
            })
            .on(Events.DC_LOST, msg => {
                if (!msg.sn || !dc.bitset) return;
                const sn = msg.sn;
                dc.bitset.delete(sn);
                this._decreBitCounts(sn);
            })
            .on(Events.DC_PIECE_ACK, msg => {
                if (msg.size) {
                    this.engine.fetcher.reportUploaded(msg.size);
                }
            })
            .on(Events.DC_PIECE, msg => {                                                  //接收到piece事件，即二进制包头
                if (this.criticalSeg && this.criticalSeg.segId === msg.seg_id) {                    //接收到critical的响应
                    this.stats.tfirst = Math.max(performance.now(), this.stats.trequest);
                }
            })
            .on(Events.DC_PIECE_NOT_FOUND, msg => {
                if (this.criticalSeg && this.criticalSeg.segId === msg.seg_id) {             //接收到critical未找到的响应
                    window.clearTimeout(this.criticaltimeouter);                             //清除定时器
                    logger.info(`DC_PIECE_NOT_FOUND`);
                    this._criticaltimeout();                                                   //触发超时，由xhr下载
                }
            })
            .on(Events.DC_RESPONSE, response => {                                            //接收到完整二进制数据
                let { level, sn, data, seg_id } = response;
                const isCritical = this.criticalSeg && this.criticalSeg.segId === seg_id && this.criticaltimeouter;
                const verified = this.config.validateSegment(level, sn, data);                           // 对数据进行校验，防止节点作恶
                if (verified) {
                    if (isCritical) {
                        logger.info(`receive criticalSeg seg_id ${seg_id}`);
                        window.clearTimeout(this.criticaltimeouter);                                 //清除定时器
                        this.criticaltimeouter = null;
                        let stats = this.stats;
                        stats.tload = Math.max(stats.tfirst, performance.now());
                        stats.loaded = stats.total = data.byteLength;
                        this.criticalSeg = null;
                        this.context.frag.fromPeerId = dc.remotePeerId;
                        this.callbacks.onSuccess(response, stats, this.context);
                    } else {
                        this.bufMgr.handleFrag(sn, level, seg_id, data, dc.remotePeerId, false);
                    }
                    this.updateLoadedSN(sn);
                } else {
                    logger.warn(`segment ${level}-${sn} validate failed`);
                    if (isCritical) {
                        window.clearTimeout(this.criticaltimeouter);                                 //清除定时器
                        this._criticaltimeout();
                    }
                }
            })
            .on(Events.DC_REQUEST, msg => {
                let segId = '';
                if (!msg.seg_id) {                                    //请求sn的request
                    segId = this.bufMgr.getSegIdbySN(msg.sn);
                } else {                                           //请求url的request
                    segId = msg.seg_id;
                }
                if (segId && this.bufMgr.hasSegOfId(segId)) {
                    let seg = this.bufMgr.getSegById(segId);
                    dc.sendBuffer(seg.sn, seg.level, seg.segId, seg.data);
                    // test
                    // setTimeout(() => {
                    //     console.warn(`sendBuffer`);
                    //     dc.sendBuffer(seg.sn, seg.level, seg.segId, seg.data);
                    // }, 20000);
                } else {
                    dc.sendJson({
                        event: Events.DC_PIECE_NOT_FOUND,
                        seg_id: segId,
                        sn: msg.sn
                    })
                }
            })
            .on(Events.DC_TIMEOUT, () => {
                // logger.warn(`DC_TIMEOUT`);
                // if (this.criticaltimeouter) {
                //     window.clearTimeout(this.criticaltimeouter);                             //清除定时器
                //     this._criticaltimeout();
                // }
            })
            .on(Events.DC_USELESS, () => { // 多次数据请求超时
                logger.warn(`datachannel ${dc.remotePeerId} download miss reach dcTolerance`);
                if (this.config.live) {
                    dc.choked = true;  // live情况下不再向此节点请求，但允许此节点的数据请求
                } else {
                    dc.close();        // VOD由于数据传输基本是单向的，所以超时多次可以直接关闭，从而可以请求更多节点
                }
            })
    }

    _setupEngine(){
        this.engine.on(Events.FRAG_LOADING, (sn) => {
            this.loadingSN = sn;
        })
            .on(Events.FRAG_LOADED, (sn) => {
                this.updateLoadedSN(sn);
            })
            .on(Events.FRAG_CHANGED, (sn) => {
                this.updatePlaySN(sn);
            })
    }

    _broadcastToPeers(msg) {
        if (this.peersNum > 0) {
            for (let peer of this.peerMap.values()) {
                peer.sendJson(msg);
            }
        }
    }

    _getIdlePeer() {
        return [...this.peerMap.values()].filter(peer => {
            return peer.isAvailable;
        });
    }

    _decreBitCounts(index) {
        if (this.bitCounts.has(index)) {
            let last = this.bitCounts.get(index);
            // this.bitCounts.set(index, last-1);
            // if (this.bitCounts.get(index) === 0) {
            //     this.bitCounts.delete(index);
            // }
            if (last === 1) {
                this.bitCounts.delete(index);
            } else {
                this.bitCounts.set(index, last-1);
            }
        }
    }

    _increBitCounts(index) {
        if (!this.bitCounts.has(index)) {
            this.bitCounts.set(index, 1);
        } else {
            let last = this.bitCounts.get(index);
            this.bitCounts.set(index, last+1);
        }
    }

    _criticaltimeout() {
        const { logger } = this.engine;
        logger.warn(`critical request timeout`);
        this.criticalSeg = null;
        this.criticaltimeouter = null;
        if (this.targetPeer) this.targetPeer.loadtimeout();
        this.callbacks.onTimeout(this.stats, this.context, null);
    }
}


export default Scheduler;